/**
 *  \file monAirportRhapsodyIDS.c (implementation file)
 *
 *  \brief Problem name: Airport rhapsody.
 *
 *  \brief Concept: António Rui Borges
 *
 *  Synchronization based on monitors.
 *  Both threads and the monitor are implemented using the pthread library which enables the creation of a
 *  monitor of the Lampson / Redell type.
 *
 *  In order the students be able to develop and test separately the operations carried out by the porter,
 *  the passengers and the bus driver, the internal data structure is also made visible.
 *
 *  Definition of monitor internal data structure.
 *
 *  \author António Rui Borges - December 2013
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <errno.h>

#include "probConst.h"
#include "probDataStruct.h"
#include "cam.h"
#include "queue.h"
#include "logging.h"

/** \brief full state of the problem */
FULL_STAT fSt;

/** \brief porter waiting for work synchronization point */
pthread_cond_t waitingFlight;

/** \brief number of passengers who have executed the operation whatShouldIDo in each plane landing */
unsigned int nPassP;

/** \brief passengers synchronization point (one per passenger) */
pthread_cond_t pass[N];

/** \brief array of the number of calls made by the porter / bus driver to each passenger */
unsigned int nCalls[N];

/** \brief bus driver waiting for starting a new journey synchronization point */
pthread_cond_t waitingDrive;

/** \brief bus driver waiting for passengers to board / unboard synchronization point */
pthread_cond_t waitingPass;

/** \brief number of passengers who have executed either the operation enterTheBus or
 *  leaveTheBus in each bus transfer
 */
unsigned int nPassD;

/** \brief locking flag which warrants mutual exclusion inside the monitor */
pthread_mutex_t accessCR = PTHREAD_MUTEX_INITIALIZER;

/** \brief signaling flag which warrants internal data is initialized exactly once */
pthread_once_t init = PTHREAD_ONCE_INIT;

/** \brief logging file name */
extern char nFic[51];

/** \brief allusion to internal function */
static void alarmCk (int);


/**
 *  \brief Internal monitor operation.
 *
 *  Initialization of the internal data structure that contains the shared information and of the logging file.
 */

void initialization (void)
{
  unsigned int k, p, i, j;                                                                     /* counting variables */
  unsigned int nTot;                                                                        /* plane load per flight */

  srandom ((unsigned int) getpid ());                                          /* initialize random number generator */

  fSt.nLand = 0;                                                                         /* initialize plane landing */
  fSt.st.porterStat = WAITING_FOR_A_PLANE_TO_LAND;            /* the porter is reading a newspaper while waiting for
                                                                                                     next assignment */
  for (k = 0; k < K; k++)
  { nTot = 0;                                                                    /* initialize plane load per flight */
    for (p = 0; p < N; p++)
    { fSt.st.passStat[k][p].stat = AT_THE_DISEMBARKING_ZONE;             /* the passenger is coming out of the plane
                                                                                                       after landing */
      if (((unsigned int) floor (9.0*random ()/RAND_MAX+1.5)) < 4)
         fSt.st.passStat[k][p].sit = TRT;                                             /* the passenger is in transit */
         else fSt.st.passStat[k][p].sit = FD;             /* the passenger has this airport as her final destination */
                                                       /* number of pieces of luggage she is supposed to be carrying */
      fSt.st.passStat[k][p].nBagsReal = (unsigned int) floor (((double) M)*random ()/RAND_MAX+0.5);
                                                             /* number of pieces of luggage she is actually carrying */
      if ((fSt.st.passStat[k][p].sit == TRT) || (fSt.st.passStat[k][p].nBagsReal == 0))
         fSt.st.passStat[k][p].nBagsAct = fSt.st.passStat[k][p].nBagsReal;       /* no need to consider missing bags */
         else if (((unsigned int) floor (9.0*random ()/RAND_MAX+1.5)) < 5)
                                                                                   /* the passenger has lost one bag */
                 fSt.st.passStat[k][p].nBagsAct = fSt.st.passStat[k][p].nBagsReal - 1;
                 else fSt.st.passStat[k][p].nBagsAct = fSt.st.passStat[k][p].nBagsReal;  /* the passenger has no bag */
      nTot += fSt.st.passStat[k][p].nBagsAct;                                                   /* update plane load */
    }
    fSt.plHold[k].nBags = nTot;                                                 /* set number of bags for plane load */
    i = 0;
    while (i < nTot)                                                                           /* fill in plane load */
    { for (p = 0; p < N; p++)
      { for (j = 0; j < fSt.st.passStat[k][p].nBagsAct; j++, i++)
          fSt.plHold[k].bag[i].id = p;
        fSt.st.passStat[k][p].nBagsAct = 0;  /* reset number of pieces of luggage the passenger is actually carrying */
      }
    }
  }
  fSt.st.driverStat = PARKING_AT_THE_ARRIVAL_TERMINAL;              /* the driver has parked at the arrival transfer
                                                                        terminal waiting for passengers to transport */
  camInit (&(fSt.convBelt));                                                     /* set conveyor belt to empty state */
  queueInit (&(fSt.busQueue));                                                       /* set bus queue to empty state */
  fSt.bus.nOccup = 0;                                                                   /* initialize bus occupation */
  for (i = 0; i < T; i++)
    fSt.bus.seat[i] = EMPTYST;                                                                 /* list seat as empty */
  fSt.nToTPassFD = 0;      /* initialize total number of passengers for whom the airport was their final destination */
  fSt.nToTPassTST = 0;                                           /* initialize total number of passengers in transit */
  fSt.nToTBagsPCB = 0;                                /* initialize total number of bags placed in the belt conveyor */
  fSt.nToTBagsPSR = 0;                                    /* initialize total number of bags placed in the storeroom */
  fSt.nToTMBags = 0;                                                      /* initialize total number of missing bags */
  fSt.dayEnded = false;                                               /* initialize flag signaling driver day's work */

  pthread_cond_init (&waitingFlight, NULL);              /* initialize porter waiting for work synchronization point */
  for (p = 0; p< N; p++)
    pthread_cond_init (&pass[p], NULL);           /* initialize passengers synchronization point (one per passenger) */
  pthread_cond_init (&waitingDrive, NULL);               /* initialize bus driver waiting for starting a new journey
                                                                                               synchronization point */
  pthread_cond_init (&waitingPass, NULL);      /* initialize bus driver waiting for passengers synchronization point */
  nPassP = 0;                       /* initialize number of passengers who have executed the operation whatShouldIDo
                                                                                               in each plane landing */
  for (p = 0; p< N; p++)
    nCalls[p] = 0;                                /* initialize number of calls made by the porter to each passenger */
  nPassD = 0;                  /* initialize number of passengers who have executed either the operation enterTheBus
                                                                                 or leaveTheBus in each bus transfer */

  struct sigaction act, oact;                                         /* action to be introduced and existing action */
  struct itimerval titv, otitv;                         /* time interval to be introduced and existing time interval */
  int stat;                                                                                   /* status of operation */

  act.sa_handler = alarmCk;                                              /* specification of signal service function */
  sigemptyset (&act.sa_mask);                /* no other signal will be blocked during the processing of this signal */
  act.sa_flags = 0;                                                                              /* default behavior */
  if (sigaction (SIGALRM, &act, &oact) != 0)                                                 /* function registering */
     { perror ("error on registering the signal function (INIT)");
       stat = EXIT_FAILURE;
       pthread_exit (&stat);
     }
  titv.it_interval.tv_sec = titv.it_value.tv_sec = 0;                                    /* time interval definition */
  titv.it_interval.tv_usec = titv.it_value.tv_usec = 100000;
  if (setitimer (ITIMER_REAL, &titv, &otitv) != 0)
     { perror ("error on registering the time interval for interruption (INIT)");
       stat = EXIT_FAILURE;
       pthread_exit (&stat);
     }


  createLog (nFic);                                                                         /* logging file creation */
}

/**
 *  \brief Signal service function.
 *
 *  Inform the bus driver he should check whether it is the right time to start the journey.
 */

static void alarmCk (int signum)
{
  int stat;                                                                                   /* status of operation */

  if (signum == SIGALRM)
     { if ((stat = pthread_mutex_lock (&accessCR)) != 0)                                            /* enter monitor */
          { errno = stat;                                                                     /* save error in errno */
            perror ("error on entering the monitor (INIT)");
            stat = EXIT_FAILURE;
            pthread_exit (&stat);
          }
       pthread_cond_signal (&waitingDrive);                      /* inform the bus driver he should check whether it
                                                                              is the right time to start the journey */
       if ((stat = pthread_mutex_unlock (&accessCR)) != 0)                                           /* exit monitor */
          { errno = stat;                                                                     /* save error in errno */
            perror ("error on exiting monitor (INIT)");
            stat = EXIT_FAILURE;
            pthread_exit (&stat);
          }
     }
     else { fprintf (stderr, "a signal other than SIGALRM was received (INIT)!\n");
            stat = EXIT_FAILURE;
            pthread_exit (&stat);
          }
}
